/*
 Time-stamp: <2009-09-28 14:29:01 henryhu>

 File: ramfs.c

 This file contains driver for a simple file system, named ramfs,
 provide functions to read/write files that are actually in memory.

*/

#ifndef RAMFS_TOOLS
#include <user/ramfs.h>
#else
#include "ramfstools.h"
#endif

static struct fs_driver_t ramfs_dev;

struct ramfs_node_t nodes[RAMFS_NODE_COUNT];
static int free_head;

void
init_ramfs_driver(void)
{
	 int i;
	 
	 for (i = 0; i != RAMFS_NODE_COUNT; ++i)
	 {
		  nodes[i].next = nodes + i + 1;
	 }

	 nodes[RAMFS_NODE_COUNT - 1].next = NULL;

	 free_head = 0;
	 
	 ramfs_dev.read = &ramfs_read;
	 ramfs_dev.write = &ramfs_write;
	 ramfs_dev.seek = &ramfs_seek;
	 ramfs_dev.open = &ramfs_open;
	 ramfs_dev.stat = &ramfs_stat;
	 ramfs_dev.close = &ramfs_close;
	 ramfs_dev.roll = &ramfs_roll;
	 ramfs_dev.create = &ramfs_create;
	 ramfs_dev.remove = &ramfs_remove;
	 ramfs_dev.move = &ramfs_move;
	 ramfs_dev.copynode = &ramfs_copynode;
}

int
ramfs_read(struct fs_node_t *f, char *buf, int n)
{
	 struct ramfs_data_t *data = (struct ramfs_data_t *)f->data;
	 struct ramfs_node_t *node = data->node;

	 mutex_lock(&node->lock);

	 if (node->type == FILE_DIR)
	 {
		  /* 读取文件名 */
		  struct ramfs_node_t *f = (struct ramfs_node_t *)data->data;

		  if (data->dirty)
		  {
			   data->dirty = false;
			   n = 0;
		  }
		  else if (f == NULL)
		  {
			   data->data = node->children;
			   n = 0;
		  }
		  else if (n == 0)
		  {
			   n = strlen(f->name);
		  }
		  else
		  {
			   int namelen = strlen(f->name);

			   if (n > namelen) n = namelen;			   
			   memmove(buf, f->name, n);
		  }
	 }
	 else
	 {
		  /* 读取数据 */
		  if (data->ptr + n > node->size)
			   n = node->size - data->ptr;
		  
		  memmove(buf, (char *)node->data + data->ptr, n);
		  data->ptr += n;
	 }

	 mutex_unlock(&node->lock);

	 return n;
}

int
ramfs_write(struct fs_node_t *f, char *buf, int n)
{
	 struct ramfs_data_t *data = (struct ramfs_data_t *)f->data;
	 struct ramfs_node_t *node = data->node;

	 int result;
	 
	 mutex_lock(&node->lock);

	 if (node->type == FILE_DIR)
	 {
		  if (n > RAMFS_NAME_LENGTH)
			   n = RAMFS_NAME_LENGTH;
		  
		  struct ramfs_node_t
			   *f = (struct ramfs_node_t *)data->data,
			   *c = node->children;

		  /* find the node */
		  while (c != NULL)
		  {
			   if (strncmp(c->name, buf, n) == 0)
			   {
					if (c->name[n] == '\0')
						 break;
			   }
			   
			   if ((c = c->next) == node->children)
					c = NULL;
		  }
		  if (f == NULL)
		  {
			   /* FIND MODE */
			   data->data = c;
			   result = (c == NULL);
		  }
		  else
		  {
			   /* RENAME MODE */
			   if (c != NULL)
					result = 1;
			   else
			   {
					memmove(f->name, buf, n);
					f->name[n] = '\0';

					result = 0;
			   }
		  }
	 }
	 else
	 {
		  if (data->ptr + n > node->size)
		  {
			   node->size = data->ptr + n;
			   /* TODO - double-buf recipe */
			   node->data = (struct ramfs_data_t *)realloc(node->data, node->size);
		  }
		  
		  memmove((char *)node->data + data->ptr, buf, n);
		  data->ptr += n;

		  result = n;
	 }

	 mutex_unlock(&node->lock);

	 return result;
}

int
ramfs_seek(struct fs_node_t *f, int dir, int n)
{
	 struct ramfs_data_t *data = (struct ramfs_data_t *)f->data;
	 struct ramfs_node_t *node = data->node;

	 mutex_lock(&node->lock);

	 if (f->type == FILE_DIR)
	 {
		  if (dir == FS_SEEK_BEGIN)
		  {
			   data->dirty = false;
			   data->data = node->children;
		  }
		  else if (dir == FS_SEEK_FIND)
		  {
			   data->dirty = false;
			   data->data = NULL;
		  }
	 }
	 else
	 {
		  switch (dir)
		  {
		  case FS_SEEK_FORWARD:
			   data->ptr += n;
			   break;
		  case FS_SEEK_BACKWARD:
			   data->ptr -= n;
			   break;
		  case FS_SEEK_BEGIN:
			   data->ptr = n;
			   break;
		  case FS_SEEK_END:
			   data->ptr = node->size - n;
			   break;
		  }
		  
		  if (data->ptr < 0)
		  {
			   n -= data->ptr;
			   data->ptr = 0;
		  }
		  else if (data->ptr > node->size)
		  {
			   n -= data->ptr - node->size;
			   data->ptr = node->size;
		  }
	 }
	 
	 mutex_unlock(&node->lock);

	 return n;
}

static struct ramfs_node_t *
get_free_node(void)
{
	 struct ramfs_node_t *result = nodes + free_head;
	 free_head = result->next - nodes;

	 /* 初始化标记和列表 */
	 mutex_init(&result->lock);

	 return result;
}

static void
free_node(struct ramfs_node_t *node)
{
	 if (node->type == FILE_NORMAL)
	 {
		  if (node->size != 0)
		  {
			   free(node->data);
		  }
	 }
	 
	 node->next = nodes + free_head;
	 free_head = node - nodes;
}

static void
inode_to_node(struct iramfs_node_t *inode, struct ramfs_node_t *node)
{
	 mutex_init(&node->lock);
	 
	 memmove(node->name, inode->name, RAMFS_NAME_LENGTH + 1);
	 node->type = inode->type;
	 node->dcode = inode->dcode;
	 node->size = inode->size;
}

static void
dec_ref(struct ramfs_node_t *node)
{
	 mutex_lock(&node->lock);

	 bool delete = false;
	 if (--node->ref_count == 0)
		  delete = true;
	 
	 mutex_unlock(&node->lock);

	 if (delete)
	 {
		  struct ramfs_node_t *cur = node->children;
		  while (cur != NULL)
		  {
			   dec_ref(cur);
			   cur = cur->next;
		  }

		  free_node(node);
	 }
}

int
import_ramfs_node(struct iramfs_node_t *inode, struct ramfs_node_t *node)
{	 
	 int offset, result = 0;
	 struct iramfs_node_t *next;
	 
	 while (1)
	 {
		  inode_to_node(inode, node);
		  offset = sizeof(struct iramfs_node_t);
		  
		  node->ref = NULL;
		  /* the meta link */
		  node->ref_count = 1;
		  node->size = inode->size;
		  
		  next = inode;
		  
		  if (inode->type == FILE_DIR)
		  {

			   struct iramfs_node_t *cur = (struct iramfs_node_t *)((char *)inode + offset);
			   
			   if (cur->level > inode->level)
			   {
					struct ramfs_node_t *children = get_free_node();
					
					children->parent = node;
					children->prev = children->next = children;
					
					offset += import_ramfs_node(cur, children);
					
					node->children = children;
			   }
			   else node->children = NULL;
		  }
		  else
		  {
			   node->data = malloc(node->size);
			   memmove(node->data, (char *)inode + offset, node->size);

			   offset += node->size;
		  }

		  result += offset;
		  next = (struct iramfs_node_t *)((char *)inode + offset);

		  if (next->level == inode->level && next->level > 0)
		  {
			   struct ramfs_node_t *n = get_free_node();
			   
			   n->parent = node->parent;

			   n->next = node->next;
			   n->prev = node;

			   node->next->prev = n;			   
			   node->next = n;
			   
			   inode = next;
			   node = n;
		  }
		  else break;
	 }
	 
	 return result;
}

int
import_ramfs(struct iramfs_node_t *r, struct fs_node_t *node)
{
	 create_ramfs(node);
	 struct ramfs_node_t
		  *root = ((struct ramfs_data_t *)node->data)->node;
	 
	 import_ramfs_node(r, root);
	 return 0;
}

static int
export_ramfs_node(void *data, void (*putch)(char, void*),
				  struct ramfs_node_t *node, int level)
{
	 struct iramfs_node_t i;
	 struct ramfs_node_t *cur;
	 int t;
	 
	 memmove(i.name, node->name, RAMFS_NAME_LENGTH + 1);
	 i.type = node->type;
	 i.level = level;
	 i.size = node->size;

	 char *tmp = (char *)&i;
	 for (t = 0; t != sizeof(struct iramfs_node_t); ++t)
	 {
		  putch(tmp[t], data);
	 }

	 switch (i.type)
	 {
	 case FILE_NORMAL:

		  for (t = 0; t != i.size; ++t)
		  {
			   putch(((char *)node->data)[t], data);
		  }

		  break;
		  
	 case FILE_DIR:

		  cur = node->children;
		  while (cur != NULL)
		  {
			   export_ramfs_node(data, putch, cur, level + 1);
			   if ((cur = cur->next) == node->children)
					break;
		  }
		  break;
	 }

	 return 0;
}

int
export_ramfs(void *data, void (*putch)(char, void*), struct fs_node_t *root)
{
	 struct ramfs_node_t *node = ((struct ramfs_data_t *)root->data)->node;

	 int offset = 0;
	 export_ramfs_node(data, putch, node, 0);

	 struct iramfs_node_t i;
	 int t;

	 memmove(i.name, "end", 4);
	 i.level = 0;
	 char *tmp = (char *)&i;
	 for (t = 0; t != sizeof(struct iramfs_node_t); ++t)
	 {
		  putch(tmp[t], data);
	 }
	 
	 return offset;
}

int
create_ramfs(struct fs_node_t *node)
{
	 struct ramfs_node_t *root = get_free_node();
	 struct ramfs_data_t *d = (struct ramfs_data_t *)malloc(sizeof(struct ramfs_data_t));
	 
	 memmove(root->name, "rfs", 4);
	 
	 root->type = FILE_DIR;
	 root->parent = root->children = NULL;
	 root->next = root->prev = root;
	 
	 node->dev = &ramfs_dev;
	 node->type = FILE_DIR;

	 root->size = 0;

	 node->data = d;
	 d->node = root;
	 d->data = NULL;
	 d->prev = d->next = d;
	 
	 root->ref = d;
	 root->ref_count = 1;
	 
	 return 0;
}

int
ramfs_roll(struct fs_node_t *d)
{
	 struct ramfs_data_t *data = (struct ramfs_data_t *)d->data;
	 struct ramfs_node_t *node = data->node;

	 int result = 0;
	 
	 mutex_lock(&node->lock);

	 data->dirty = false;

	 struct ramfs_node_t *f = (struct ramfs_node_t *)data->data;
	 if (f == NULL) f = data->data = node->children;
	 
	 if (f == NULL)
		  result = E_RESET;
	 else if ((data->data = f->next) == node->children)
	 {
		  result = E_RESET;
	 }

	 mutex_unlock(&node->lock);

	 return result;
}

int
ramfs_stat(struct fs_node_t *d, struct fs_stat_t *stat)
{
	 struct ramfs_data_t *data = (struct ramfs_data_t *)d->data;
	 struct ramfs_node_t *node = data->node;

	 int result;

	 mutex_lock(&node->lock);

	 struct ramfs_node_t *f = (struct ramfs_node_t *)data->data;
	 
	 if (data->dirty)
	 {
		  data->dirty = false;
		  result = E_RESET;
	 }
	 else if (f == NULL)
	 {
		  data->data = node->children;
		  result = E_RESET;
	 }
	 else
	 {
		  result = 0;
		  stat->type = f->type;
		  stat->size = f->size;
	 }
	 
	 mutex_unlock(&node->lock);

	 return result;
}

int
ramfs_open(struct fs_node_t *d, struct fs_node_t *f)
{
	 struct ramfs_data_t *data = (struct ramfs_data_t *)d->data;
	 struct ramfs_node_t *node = data->node;

	 int result;

	 /* 打开文件 */
	 mutex_lock(&node->lock);

	 struct ramfs_node_t *rf = (struct ramfs_node_t *)data->data;
	 struct ramfs_data_t *fd;

	 if (data->dirty)
	 {
		  data->dirty = false;
		  result = E_RESET;
	 }
	 else if (rf == NULL)
	 {
		  data->data = node->children;
		  result = E_RESET;
	 }
	 else
	 {
		  f->type = rf->type;
		  f->dev  = &ramfs_dev;
		  
		  fd = (struct ramfs_data_t *)malloc(sizeof(struct ramfs_data_t));
		  
		  f->data = (void *)fd;
		  fd->node = rf;
		  
		  if (rf->type == FILE_DIR)
			   fd->data = rf->children;
		  else fd->ptr = 0;

		  result = 0;
	 }
		  
	 mutex_unlock(&node->lock);

	 if (result != 0) return result;
	 
	 /* 建立关联 */
	 mutex_lock(&rf->lock);

	 if (rf->ref == NULL)
	 {
		  rf->ref = fd;
		  fd->next = fd->prev = fd;
	 }
	 else
	 {
		  fd->next = rf->ref;
		  fd->prev = fd->next->prev;

		  fd->next->prev = fd;
		  fd->prev->next = fd;
	 }

	 ++rf->ref_count;
	 
	 mutex_unlock(&rf->lock);
	 
	 return result;
}

int
ramfs_close(struct fs_node_t *f)
{
	 struct ramfs_data_t *data = (struct ramfs_data_t *)f->data;
	 struct ramfs_node_t *node = data->node;

	 mutex_lock(&node->lock);

	 if (data->next != data)
	 {
		  data->next->prev = data->prev;
		  data->prev->next = data->next;

		  if (node->ref == data)
			   node->ref = data->next;
	 }
	 else
	 {
		  node->ref = NULL;
	 }

	 mutex_unlock(&node->lock);

	 dec_ref(node);

	 return 0;
}

int
ramfs_create(struct fs_stat_t *attr, char *filename, int n, struct fs_node_t *d)
{
	 if (d->type != FILE_DIR)
		  return -1;

	 struct ramfs_data_t *data = (struct ramfs_data_t *)d->data;
	 struct ramfs_node_t *node = data->node;

	 if (n > RAMFS_NAME_LENGTH) n = RAMFS_NAME_LENGTH;
	 char name[RAMFS_NAME_LENGTH + 1];

	 int result;
	 
	 memmove(name, filename, n);
	 name[n] = '\0';

	 mutex_lock(&node->lock);

	 struct ramfs_node_t *c = node->children;
	 while (c != NULL)
	 {
		  if (strcmp(c->name, name) == 0)
			   break;
		  
		  if ((c = c->next) == node->children)
			   c = NULL;
	 }

	 if (c == NULL)
	 {
		  c = get_free_node();
		  
		  c->ref = NULL;
		  		  
		  c->parent = node;

		  if (node->children == NULL)
		  {
			  c->next = c->prev = c;
		  }
		  else
		  {
			  c->next = node->children;
			  c->prev = c->next->prev;

			  c->next->prev = c;
			  c->prev->next = c;
		  }

		  node->children = c;

		  if (attr->type == FILE_DIR)
		  {
			   c->children = NULL;
		  }

		  memmove(c->name, name, n + 1);

		  c->type = attr->type;
		  c->size = 0;
		  c->data = NULL;

		  c->ref_count = 1;
		  
		  ++node->ref_count;
		  ++node->size;

		  result = 0;
	 }
	 else
	 {
		  /* 如果已经存在, 则定位 */
		  result = 1;
	 }
	 data->data = c;
	 
	 mutex_unlock(&node->lock);

	 return result;
}

int
ramfs_remove(struct fs_node_t *d)
{
	 if (d->type != FILE_DIR)
		  return -1;

	 struct ramfs_data_t *data = (struct ramfs_data_t *)d->data;
	 struct ramfs_node_t *node = data->node;

	 int result;
	 
	 mutex_lock(&node->lock);

	 struct ramfs_node_t *f = (struct ramfs_node_t *)data->data;
	 if (data->dirty)
	 {
		  data->dirty = false;
		  result = E_RESET;
	 }
	 else if (f == NULL)
	 {
		  data->data = node->children;
		  result = E_RESET;
	 }
	 else
	 {
		  struct ramfs_data_t *cur = data->next;
		  struct ramfs_node_t *rep = f->prev == f ? NULL : f->prev;
		  /* 刷新其他文件指针 */
		  while (cur != data)
		  {
			   if (cur->data == f)
			   {
					cur->data = rep;
					cur->dirty = true;
			   }
			   cur = cur->next;
		  }

		  data->dirty = true;
		  data->data = rep;

		  if (node->children == f)
			   node->children = rep;

		  if (f->next != f)
		  {
			  f->next->prev = f->prev;
			  f->prev->next = f->next;
		  }
		  
		  f->parent = NULL;

		  --node->size;
		  
		  result = 0;
	 }

	 mutex_unlock(&node->lock);

	 if (result != 0) return result;
	 
	 dec_ref(f);
	 dec_ref(node);
	 
	 return result;
}

int
ramfs_move(struct fs_node_t *s, struct fs_node_t *d)
{
	 if (s->type != FILE_DIR || d->type != FILE_DIR)
		  return -1;

	 struct ramfs_data_t *data = (struct ramfs_data_t *)d->data;
	 struct ramfs_node_t *node = data->node;

	 int result;

	 mutex_lock(&node->lock);

	 struct ramfs_node_t *f = (struct ramfs_node_t *)data->data;
	 if (data->dirty)
	 {
		  data->dirty = false;
		  return E_RESET;
	 }
	 else if (f == NULL)
	 {
		  data->data = node->children;
		  result = E_RESET;
	 }
	 
	 mutex_unlock(&node->lock);

	 if (result != 0)
		  return result;
	 
	 /* 剪切 */
	 data = (struct ramfs_data_t *)s->data;
	 node = data->node;

	 mutex_lock(&node->lock);

	 f = (struct ramfs_node_t *)data->data;
	 if (data->dirty)
	 {
		  data->dirty = false;
		  return E_RESET;
	 }
	 else if (f == NULL)
	 {
		  data->data = node->children;
		  result = E_RESET;
	 }
	 else
	 {
		  struct ramfs_data_t *cur = data->next;
		  struct ramfs_node_t *rep = f->prev == f ? NULL : f->prev;
		  /* 刷新其他文件指针 */
		  while (cur != data)
		  {
			   if (cur->data == f)
			   {
					cur->data = rep;
					cur->dirty = true;
			   }
			   cur = cur->next;
		  }
		  
		  if (node->children == f)
			   node->children = rep;

		  if (f->next != f)
		  {
			   f->next->prev = f->prev;
			   f->prev->next = f->next;
		  }

		  -- node->size;
		  
		  result = 0;
	 }

	 if (data->prev != data)
	 {
		  data->prev->next = data->next;
		  data->next->prev = data->prev;

		  if (node->ref == data)
			   node->ref = data->next;
	 }
	 else
	 {
		  node->ref = NULL;
	 }
	 
	 mutex_unlock(&node->lock);

	 struct ramfs_data_t *sdata;

	 if (result != 0)
	 {
		  return result;
	 }
	 else
	 {
		  dec_ref(node);
		  sdata = data;
	 }

	 /* 粘贴 */
	 data = (struct ramfs_data_t *)d->data;
	 node = data->node;

	 mutex_lock(&node->lock);

	 struct ramfs_node_t *c = (struct ramfs_node_t *)data->data;

	 struct ramfs_data_t *cur = data->next;
	 struct ramfs_node_t *rep = c->prev == c ? NULL : c->prev;
	 while (cur != data)
	 {
		  if (cur->data == c)
		  {
			   cur->data = rep;
			   cur->dirty = true;
		  }
		  cur = cur->next;
	 }
	 data->data = NULL;

	 /* meta 环 */
	 f->prev = c->prev;
	 f->prev->next = f;
	 f->next = c->next;
	 f->next->prev = f;
	 
	 f->parent = node;
	 if (node->children == c) node->children = f;
	 
	 c->parent = NULL;

	 /* ref 环 */
	 sdata->node = node;
	 
	 sdata->next = data;
	 sdata->prev = data->prev;

	 data->prev = sdata;
	 sdata->prev->next = sdata;

	 data->data = node->children;

	 /* 改名 */
	 memmove(f->name, c->name, RAMFS_NAME_LENGTH + 1);
	 
	 mutex_unlock(&node->lock);
	 
	 dec_ref(c);

	 return 0;
}

int
ramfs_copynode(struct fs_node_t *src, struct fs_node_t *dst)
{
	 struct ramfs_data_t *data = (struct ramfs_data_t *)src->data;
	 struct ramfs_node_t *node = data->node;

	 struct ramfs_data_t *newdata = malloc(sizeof(struct ramfs_data_t));
	 dst->data = (void*) newdata;

	 newdata->node = node;
	 node->ref_count++;

	 data->next->prev = newdata;
	 newdata->next = data->next;
	 data->next = newdata;
	 newdata->prev = data;

	 newdata->dirty = data->dirty;
	 newdata->data = data->data;

	 return 0;
}

